package com.cskaoyan.duolai.clean.market.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cskaoyan.duolai.clean.common.constants.ErrorInfo;
import com.cskaoyan.duolai.clean.common.expcetions.BadRequestException;
import com.cskaoyan.duolai.clean.common.expcetions.CommonException;
import com.cskaoyan.duolai.clean.common.expcetions.DBException;
import com.cskaoyan.duolai.clean.common.model.dto.PageDTO;
import com.cskaoyan.duolai.clean.common.utils.*;
import com.cskaoyan.duolai.clean.market.client.CommonUserApi;
import com.cskaoyan.duolai.clean.market.converter.CouponConverter;
import com.cskaoyan.duolai.clean.market.dto.AvailableCouponsDTO;
import com.cskaoyan.duolai.clean.market.dto.CouponUseDTO;
import com.cskaoyan.duolai.clean.market.enums.CouponStatusEnum;
import com.cskaoyan.duolai.clean.market.config.RedissonLuaHandler;
import com.cskaoyan.duolai.clean.market.dao.mapper.CouponMapper;
import com.cskaoyan.duolai.clean.market.dao.entity.ActivityDO;
import com.cskaoyan.duolai.clean.market.dao.entity.CouponDO;
import com.cskaoyan.duolai.clean.market.dao.entity.CouponWriteOffDO;
import com.cskaoyan.duolai.clean.market.request.CouponPageRequest;
import com.cskaoyan.duolai.clean.market.request.SeizeCouponCommand;
import com.cskaoyan.duolai.clean.market.dto.ActivityInfoDTO;
import com.cskaoyan.duolai.clean.market.dto.CouponDTO;
import com.cskaoyan.duolai.clean.market.dto.SeizeCouponResultDTO;
import com.cskaoyan.duolai.clean.market.request.CouponUseParam;
import com.cskaoyan.duolai.clean.market.service.IActivityService;
import com.cskaoyan.duolai.clean.market.service.ICouponService;
import com.cskaoyan.duolai.clean.market.service.ICouponWriteOffService;
import com.cskaoyan.duolai.clean.market.utils.CouponUtils;
import com.cskaoyan.duolai.clean.mvc.utils.UserContext;
import com.cskaoyan.duolai.clean.mysql.utils.PageUtils;
import com.cskaoyan.duolai.clean.rocketmq.client.RocketMQClient;
import com.cskaoyan.duolai.clean.rocketmq.constant.MqTopicConstant;
import com.cskaoyan.duolai.clean.user.dto.CommonUserDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;


/**
 * <p>
 * 服务实现类
 * </p>
 */
@Service
@Slf4j
public class CouponServiceImpl extends ServiceImpl<CouponMapper, CouponDO> implements ICouponService {

    @Resource
    private RedissonLuaHandler redissonLuaHandler;


    @Resource
    private IActivityService activityService;

    @Resource
    private ICouponWriteOffService couponWriteOffService;

    @Resource
    private CouponConverter couponConverter;

    @Resource
    private RocketMQClient rocketMQClient;

    @Resource
    private ICouponService couponService;

    @Resource
    private CommonUserApi commonUserApi;

    @Override
    public PageDTO<CouponDTO> queryForPageOfOperation(CouponPageRequest couponPageRequestDTO) {
        // 1.数据校验
        if (ObjectUtils.isNull(couponPageRequestDTO.getActivityId())) {
            throw new BadRequestException("请指定活动");
        }
        // 2.数据查询
        // 分页 排序
        Page<CouponDO> couponQueryPage = PageUtils.parsePageQuery(couponPageRequestDTO, CouponDO.class);
        // 查询条件
        LambdaQueryWrapper<CouponDO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(CouponDO::getActivityId, couponPageRequestDTO.getActivityId());
        // 查询数据
        Page<CouponDO> couponPage = baseMapper.selectPage(couponQueryPage, lambdaQueryWrapper);

        // 3.数据转化，并返回
        return couponConverter.toCouponInfoResPage(couponPage.getRecords(),
                (int) couponPage.getTotal(), (int) couponPage.getPages());
    }

    @Override
    public List<CouponDTO> queryForList(Long lastId, Long userId, Integer status) {
        //  游标分页 考虑用lastId做排序字段 查询优惠劵列表

        // 检验状态
        if(status > CouponStatusEnum.VOIDED.getStatus() || status < CouponStatusEnum.NO_USE.getStatus()){
            throw  new BadRequestException("请求状态不存在");
        }
        //构建查询条件
        LambdaQueryWrapper<CouponDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(CouponDO::getUserId,userId)
                .lt(ObjectUtils.isNotNull(lastId),CouponDO::getId,lastId)  // 用大于或小于 替换掉原来的offset
                .eq(CouponDO::getStatus,status)
                .orderByDesc(CouponDO::getId)
                .last("limit 10");
        List<CouponDO> couponDOS = baseMapper.selectList(queryWrapper);

        if(CollUtils.isEmpty(couponDOS)){
            return CollUtils.emptyList();
        }

        return couponConverter.couponsToCouponInfoDTOs(couponDOS);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void revoke(Long activityId) {
        lambdaUpdate()
                .set(CouponDO::getStatus, CouponStatusEnum.VOIDED.getStatus())
                .eq(CouponDO::getActivityId, activityId)
                .eq(CouponDO::getStatus, CouponStatusEnum.NO_USE.getStatus())
                .update();
    }

    @Override
    public Integer countReceiveNumByActivityId(Long activityId) {
        return lambdaQuery().eq(CouponDO::getActivityId, activityId)
                .count().intValue();
    }

    @Override
    public void processExpireCoupon() {
        lambdaUpdate()
                .set(CouponDO::getStatus, CouponStatusEnum.INVALID.getStatus())
                .eq(CouponDO::getStatus, CouponStatusEnum.NO_USE.getStatus())
                .le(CouponDO::getValidityTime, DateUtils.now())
                .update();
    }

    @Override
    public void seizeCoupon(SeizeCouponCommand seizeCouponCommand) {
        //todo
        // 检验
        ActivityInfoDTO activity = activityService.getActivityInfoByIdFromCache(seizeCouponCommand.getId());
        LocalDateTime now = DateUtils.now();

        if(activity == null || activity.getDistributeStartTime().isAfter(now)){
            throw new CommonException(ErrorInfo.Code.SEIZE_COUPON_FAILD,"活动未开始");
        }
        if(activity == null || activity.getDistributeEndTime().isBefore(now)){
            throw new CommonException(ErrorInfo.Code.SEIZE_COUPON_FAILD,"活动已结束");
        }
        // 抢卷

        log.debug("dispatch coupon,activityId ->{},USerContext.currentUserId():{}",
                seizeCouponCommand.getId(),UserContext.currentUserId());

        Long result = redissonLuaHandler.seizeCoupon(seizeCouponCommand.getId(), UserContext.currentUserId());
        log.debug("dispatch coupon result:{}",result);
        if(result == -1){
            throw new CommonException(ErrorInfo.Code.SEIZE_COUPON_FAILD,"限领一张");
        }
        if(result == -2){
            throw new CommonException(ErrorInfo.Code.SEIZE_COUPON_FAILD,"优惠劵已抢完");
        }

        // 数据同步(异步更新)

        SeizeCouponResultDTO seizeCouponResultDTO = new SeizeCouponResultDTO();
        seizeCouponResultDTO.setActivityId(seizeCouponCommand.getId());
        seizeCouponResultDTO.setUserId(UserContext.currentUserId());
        rocketMQClient.sendMessage(MqTopicConstant.COUPON_SEIZE_TOPIC,seizeCouponResultDTO);

        // 谁去消费？ CouponSeizeConsumer


    }

    @Override
    public List<AvailableCouponsDTO> getAvailable(BigDecimal totalAmount) {
        //  获取可用优惠劵列表  totalAmount 总金额
        Long userId = UserContext.currentUserId();

        LambdaQueryWrapper<CouponDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(CouponDO::getUserId,userId)
                .eq(CouponDO::getStatus,CouponStatusEnum.NO_USE.getStatus())
                .gt(CouponDO::getValidityTime,DateUtils.now())
                .le(CouponDO::getAmountCondition,totalAmount); // 满减金额小于 总金额
        List<CouponDO> coupons = baseMapper.selectList(queryWrapper);
        if(CollUtils.isEmpty(coupons)){
            return new ArrayList<>();
        }

        List<AvailableCouponsDTO> availableCouponsDTO = coupons.stream()
                .peek(couponDO -> couponDO.setDiscountAmount(CouponUtils.calDiscountAmount(couponDO, totalAmount)))
                .filter(couponDO -> couponDO.getDiscountAmount().compareTo(new BigDecimal(0)) > 0 &&
                        couponDO.getDiscountAmount().compareTo(totalAmount) < 0)
                .sorted(Comparator.comparing(CouponDO::getDiscountAmount))
                .map(couponDO -> couponConverter.couponDoToAvailableCouponsDTO(couponDO))
                .collect(Collectors.toList());

        return availableCouponsDTO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public CouponUseDTO use(CouponUseParam couponUseCommand) {
        // 优惠劵使用
        if(ObjectUtil.isNull(couponUseCommand.getOrdersId())){
            throw new BadRequestException("优惠劵核销的订单信息为空");
        }
        Long userId = UserContext.currentUserId();
        CouponDO couponDO = baseMapper.selectById(couponUseCommand.getId());
        if(couponDO == null){
            throw new BadRequestException("优惠劵不存在");
        }
        if(ObjectUtil.notEqual(couponDO.getUserId(),userId)){
            throw new BadRequestException("只允许核销自己的优惠劵");
        }

        LambdaUpdateWrapper<CouponDO> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(CouponDO::getId,couponUseCommand.getId())
                .eq(CouponDO::getStatus,CouponStatusEnum.NO_USE.getStatus())
                .gt(CouponDO::getValidityTime,DateUtils.now())
                .lt(CouponDO::getAmountCondition,couponUseCommand.getTotalAmount())
                .set(CouponDO::getOrdersId,couponUseCommand.getOrdersId())
                .set(CouponDO::getStatus,CouponStatusEnum.USED.getStatus())
                .set(CouponDO::getUseTime,LocalDateTime.now());
        int update = baseMapper.update(couponDO, updateWrapper);
        if(update <= 0){
            throw new DBException("优惠劵核销失败");
        }
//        boolean update = lambdaUpdate() // 补充条件
//                .update();
//        if(!update){
//            throw new DBException("优惠劵核销失败");
//        }
        //
        CouponWriteOffDO couponWriteOffDo = CouponWriteOffDO.builder()
                .id(IdUtil.getSnowflakeNextId())
                .couponId(couponUseCommand.getId())
                .userId(userId)
                .ordersId(couponUseCommand.getOrdersId())
                .activityId(couponDO.getActivityId())
                .writeOffTime(DateUtils.now())
                .writeOffManName(couponDO.getUserName())
                .writeOffManPhone(couponDO.getUserPhone())
                .build();
        if(!couponWriteOffService.save(couponWriteOffDo)){
            throw new DBException("优惠劵核销信息保存失败");
        }

        BigDecimal discountAmount = CouponUtils.calDiscountAmount(couponDO, couponUseCommand.getTotalAmount());

        CouponUseDTO couponUseDTO = new CouponUseDTO();
        couponUseDTO.setDiscountAmount(discountAmount);

        return couponUseDTO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void syncCouponSeizeInfo(SeizeCouponResultDTO couponSeizeInfo) {
        // 信息的检验
        ActivityDO activityDO = activityService.getById(couponSeizeInfo.getActivityId());
        if(activityDO == null){
            return ;
        }

        CommonUserDTO commonUserDTO = commonUserApi.findById(couponSeizeInfo.getUserId());
        if(commonUserDTO == null ){
            return ;
        }
        // 业务处理

        CouponDO coupon = CouponDO.builder().activityId(couponSeizeInfo.getActivityId())
                .userId(couponSeizeInfo.getUserId())
                .userName(commonUserDTO.getNickname())
                .userPhone(commonUserDTO.getPhone())
                .name(activityDO.getName())
                .type(activityDO.getType())
                .discountAmount(activityDO.getDiscountAmount())
                .discountRate(activityDO.getDiscountRate())
                .amountCondition(activityDO.getAmountCondition())
                // 有效时间
                .validityTime(DateUtils.now().plusDays(activityDO.getValidityDays()))
                // 使用状态
                .status(CouponStatusEnum.NO_USE.getStatus())
                .build();

        // 保存抢卷信息
        int insert = baseMapper.insert(coupon);

        // 扣减库存
        activityService.deductStock(couponSeizeInfo.getActivityId());

    }

}
